package cn.edu.fzu.homemaking.sso.service.impl;

import static cn.edu.fzu.homemaking.redis.RedisKey.vCodeKey;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;

import cn.edu.fzu.homemaking.common.MD5Util;
import cn.edu.fzu.homemaking.common.SessionConstant;
import cn.edu.fzu.homemaking.exception.ServiceException;
import cn.edu.fzu.homemaking.helper.EmailHelper;
import cn.edu.fzu.homemaking.helper.FaceVerifyHelper;
import cn.edu.fzu.homemaking.helper.UploadHelper;
import cn.edu.fzu.homemaking.helper.VcodeHelper;
import cn.edu.fzu.homemaking.sso.domain.entity.CompanyUser;
import cn.edu.fzu.homemaking.sso.domain.entity.SysUser;
import cn.edu.fzu.homemaking.sso.domain.entity.User;
import cn.edu.fzu.homemaking.sso.domain.enums.CompanyUserTypeEnum;
import cn.edu.fzu.homemaking.sso.domain.enums.SexEnum;
import cn.edu.fzu.homemaking.sso.domain.enums.StatusEnum;
import cn.edu.fzu.homemaking.sso.service.CompanyService;
import cn.edu.fzu.homemaking.sso.service.LoginService;
import cn.edu.fzu.homemaking.sso.service.UserService;
import cn.edu.fzu.homemaking.web.RetCode;

@Component
public class LoginServiceImpl implements LoginService {

    @Value("${file-url}")
    private String                         fileUrl;

    @Resource
    private UserService                    userService;
    @Resource
    private CompanyService                 companyService;

    @Resource
    private FaceVerifyHelper               faceVerifyHelper;

    @Resource
    private EmailHelper                    emailHelper;

    //保存用户登录信息<email,session>
    private final Map<String, HttpSession> sessionMap  = new HashMap<>();

    //邮箱正则校验
    private static final String            EMAIL_REGEX = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$";

    @Override
    public void login(String email, String password, String code, HttpSession session) {
        String vcode = (String) session.getAttribute(vCodeKey(session.getId()));
        if (!StringUtils.equalsIgnoreCase(code, vcode)) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "验证码错误");
        }
        User user = userService.getUserByEmail(email, (String) session.getAttribute(SessionConstant.USER_TYPE));
        if (user == null) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "用户不存在");
        }
        if (user.getStatus() == StatusEnum.BAN || user.getStatus() == StatusEnum.DELETE) {
            throw new ServiceException(RetCode.REFUSE_LOGIN, "禁止访问");
        }
        if (!user.getPassword().equals(MD5Util.MD5Encode(password))) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "密码错误");
        }
        login(session, user);
    }


    @Override
    public void faceLogin(String face, HttpSession session) {
        Map<String, String> allFace = userService.allFace();
        for (Map.Entry<String, String> entry : allFace.entrySet()) {
            String email = entry.getKey();
            String faceUrl = entry.getValue();
            if (faceVerifyHelper.isSameFace(face, faceUrl)) {
                User user = userService.getUserByEmail(email, (String) session.getAttribute(SessionConstant.USER_TYPE));
                login(session, user);
                return;
            }
        }
        throw new ServiceException(RetCode.PARAMETER_ERROR, "未匹配相关用户");
    }


    @Override
    public String imgCode(String email, HttpSession session) throws IOException {
        VcodeHelper helper = new VcodeHelper(5);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String code = helper.generatorVCode();

        BufferedImage generatorVCodeImage = helper.generatorRotateVCodeImage(code, true);
        ImageIO.write(generatorVCodeImage, "gif", outputStream);
        session.setAttribute(vCodeKey(session.getId()), code);
        return Base64Utils.encodeToString(outputStream.toByteArray());
    }


    @Override
    public void logout(HttpSession session) {
        User user = (User) session.getAttribute(SessionConstant.ACCOUNT);
        if (user != null) {
            sessionMap.remove(user.getEmail());
        }
        session.invalidate();
    }


    @Override
    public void register(String email, String password, String name, SexEnum sex, Date birth, String phone,
                         String address, String faceUrl, String code, HttpSession session) {
        String emailCode = (String) session.getAttribute(email);
        if (!StringUtils.equalsIgnoreCase(code, emailCode)) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "邮箱验证码错误");
        }
        userService.addCustomer(email, password, name, sex, birth, phone, address, faceUrl);
    }


    @Override
    public String uploadFace(MultipartFile face) {
        if (face == null) {
            throw new ServiceException("请上传人脸照片");
        }
        try {
            return UploadHelper.upload(String.valueOf(System.currentTimeMillis()), face, fileUrl);
        } catch (IOException e) {
            throw new ServiceException("系统异常，请稍候重试");
        }
    }


    @Override
    public void sendEmailCode(String email, HttpSession session) {
        if (StringUtils.isBlank(email) || !Pattern.compile(EMAIL_REGEX).matcher(email).matches()) {
            throw new ServiceException("请正确填写邮箱");
        }
        VcodeHelper codeHelper = new VcodeHelper(5);
        String code = codeHelper.generatorVCode();
        emailHelper.sendMail(email, "邮箱验证码", "您的此次验证码为：" + code + ",有效时长为30分钟");
        session.setAttribute(email, code);
    }


    @Transactional
    @Override
    public void registerCompanyAdmin(String email, String password, String name, SexEnum sex, Date birth, String phone,
                                     String address, String faceUrl, String code, String companyName, String creditCode,
                                     String legalPerson, String businessLicense, String contact, String companyPhone,
                                     Long areaId, String companyAddress, HttpSession session) {
        String emailCode = (String) session.getAttribute(email);
        if (!StringUtils.equalsIgnoreCase(code, emailCode)) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "邮箱验证码错误");
        }
        Long companyId = companyService.add(companyName, creditCode, legalPerson, businessLicense, contact,
                companyPhone, areaId, companyAddress);
        userService.addCompanyUser(email, password, name, sex, birth, phone, address, faceUrl, companyId, null,
                CompanyUserTypeEnum.ADMIN, BigDecimal.ZERO);
    }


    @Override
    public void updatePassword(String email, String code, String password, HttpSession session) {
        String emailCode = (String) session.getAttribute(email);
        if (!StringUtils.equalsIgnoreCase(code, emailCode)) {
            throw new ServiceException(RetCode.PARAMETER_ERROR, "邮箱验证码错误");
        }
        User user = userService.getUserByEmail(email, null);
        userService.updateUser(user, null, MD5Util.MD5Encode(password), null, null, null, null);
    }


    private void login(HttpSession session, User user) {
        session.setAttribute(SessionConstant.ACCOUNT, user);

        HttpSession se = sessionMap.get(user.getEmail());
        if (se != null && !Objects.equals(session.getId(), se.getId())) {
            //踢出异地登录用户
            logout(se);
        }
        if (user instanceof SysUser) {
            session.setAttribute(SessionConstant.USER_TYPE, SessionConstant.SYS_USER);
        } else if (user instanceof CompanyUser) {
            session.setAttribute(SessionConstant.USER_TYPE, SessionConstant.COMPANY);
        } else {
            session.setAttribute(SessionConstant.USER_TYPE, SessionConstant.USER);
        }
        sessionMap.put(user.getEmail(), session);
    }
}
